using Pinwheel.LSerialize;
using Pinwheel.Service.Protocol;

namespace Pinwheel.Kernel
{
    public interface IConnection
    {
        IServiceManager ServiceManager { get; }
        
        PID Agent { get; }
        
        byte[] Encode(NetPacket packet);
        
        NetPacket Decode(byte[] data);
        
        void SendSync(string service, ServiceMessage message);
        
        void SendSync(NetPacket packet);

        Task SendAsync(string service, ServiceMessage message);

        Task SendAsync(NetPacket packet);

        void SetAgent(PID agent);

        void Close();
    }
    
    public class Connection : IConnection
    {
        IServiceManager m_ServiceManager;
        ISocketSession m_Session;
        PID m_Agent;
        
        public IServiceManager ServiceManager => m_ServiceManager;
        public PID Agent => m_Agent;
        
        public Connection(IServiceManager manager,ISocketSession session)
        {
            m_ServiceManager = manager;
            m_Session = session;
        }
        
        public byte[] Encode(NetPacket packet)
        {
            // Log.Info($"Connection.Encode packet: {packet.Type} {packet.Session} {packet.Data.Length}");
            // var buffer = LSerializer.Serialize(ref packet);
            // Log.Info($"Connection.Encode buffer: {BitConverter.ToString(buffer)})");
            // return buffer;
            return LSerializer.Serialize(ref packet);
        }

        public NetPacket Decode(byte[] data)
        {
            // Log.Info($"Connection.Decode buffer: {BitConverter.ToString(data)})");
            var packet = LSerializer.Deserialize<NetPacket>(data);
            // Log.Info($"Connection.Decode packet: {packet.Type} {packet.Session} {packet.Data.Length}");
            return packet;
        }
        
        public void SendSync(string service, ServiceMessage message)
        {
            var packet = new NetPacket();
            var data = LSerializer.Serialize(ref message);
            // var msg = LSerializer.Deserialize<ServiceMessage>(data);
            // Assert.Check(msg.Session == message.Session, $"Connection.SendSync: {msg.Session} != {message.Session}");
            // Assert.Check(msg.Type == message.Type, $"Connection.SendSync: {msg.Type} != {message.Type}");
            // Assert.Check(msg.Method == message.Method, $"Connection.SendSync: {msg.Method} != {message.Method}");
            // Assert.Check(msg.Source == message.Source, $"Connection.SendSync: {msg.Source} != {message.Source}");
            // Assert.Check(msg.Destination == message.Destination, $"Connection.SendSync: {msg.Destination} != {message.Destination}");
            packet.SetType((byte)message.Type);
            packet.SetSession(message.Session);
            packet.SetName(service);
            packet.SetData(data);
            m_Session.Send(Encode(packet));
        }
        
        public void SendSync(NetPacket packet)
        {
            m_Session.Send(Encode(packet));
        }

        public Task SendAsync(NetPacket packet)
        {
            var factory = null! != ServiceManager ? ServiceManager.Factory : Task.Factory;
            return factory.StartNew(() => SendSync(packet));
        }

        public Task SendAsync(string service, ServiceMessage message)
        {
            var factory = null! != ServiceManager ? ServiceManager.Factory : Task.Factory;
            return factory.StartNew(() => SendSync(service, message));
        }
        
        public void SetAgent(PID agent)
        {
            m_Agent = agent;
        }

        public void Close()
        {
            if (m_Session.Socket is IServerSocket serverSocket)
            {
                serverSocket.Close(m_Session.ReceiveEventArgs);
            }
            else
            {
                m_Session.Socket.Close();
            }
        }
    }
}